package com.ctshk.gateway.filter;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.ctshk.common.constant.RedisConstants;
import com.ctshk.common.dto.Result;
import com.ctshk.common.enums.SystemError;
import com.ctshk.common.model.TokenUser;
import com.ctshk.common.utils.RSAUtil;
import com.ctshk.gateway.config.WhiteListConfig;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.Charset;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @description token过滤器
 * @author 谢诗宏
 * @date 2020/12/10
 */
@Slf4j
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    @Value("${auth.user.token.timeout.app}")
    private Long tokenTimeoutApp;

    @Value("${auth.user.token.timeout.sapp}")
    private Long tokenTimeoutSapp;

    static private final String URI_PREFIX_APP = "/app";
    static private final String URI_PREFIX_SAPP = "/crm";

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private WhiteListConfig whiteListConfig;
    AntPathMatcher antPathMatcher = new AntPathMatcher();

    @SneakyThrows
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();

        String path = request.getURI().getPath();
        // 判断是否是过滤的路径, 是的话放行,C端也全部放行
        if (path.contains(URI_PREFIX_APP) || isExclusionUrl(path)) {
            return chain.filter(exchange);
        }

        String token = exchange.getRequest().getHeaders().getFirst(RedisConstants.JWT_TOKEN_HEADER);
        if (StrUtil.isBlank(token)) {
            ServerHttpResponse response = exchange.getResponse();
            String body = JSONUtil.toJsonStr(Result.failed(SystemError.USER_1000));
            DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(Charset.forName("UTF-8")));
            return response.writeWith(Mono.just(buffer));
        }

        token = token.replace(RedisConstants.JWT_TOKEN_PREFIX, Strings.EMPTY);
        String tokenDecrypt = null;
        try {
            tokenDecrypt = RSAUtil.decryptStr(token);
        } catch (Exception e) {
            ServerHttpResponse response = exchange.getResponse();
            String body = JSONUtil.toJsonStr(Result.failed(SystemError.USER_1015));
            DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(Charset.forName("UTF-8")));
            return response.writeWith(Mono.just(buffer));
        }
        TokenUser tokenUser = JSONObject.parseObject(tokenDecrypt, TokenUser.class);
        //如果token不存在就是失效或者未登录
        if (!redisTemplate.hasKey(RedisConstants.TOKEN_USER_KEY + tokenUser.getId())) {
            log.info(tokenUser.getUsername()+"=======失效的TOKEN:"+RedisConstants.TOKEN_USER_KEY + tokenUser.getId());
            ServerHttpResponse response = exchange.getResponse();
            String body = JSONUtil.toJsonStr(Result.failed(SystemError.USER_1009));
            DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(Charset.forName("UTF-8")));
            return response.writeWith(Mono.just(buffer));
        }

        if (path.contains(URI_PREFIX_APP)) {
            // APP端超时有效期
            redisTemplate.opsForValue().set(RedisConstants.TOKEN_USER_KEY + tokenUser.getId(), token, tokenTimeoutApp, TimeUnit.DAYS);
        } else if (path.contains(URI_PREFIX_SAPP)) {
            log.info("重置登錄有效時間："+tokenTimeoutSapp+"分鐘");
            // SAPP端超时有效期
            redisTemplate.opsForValue().set(RedisConstants.TOKEN_USER_KEY + tokenUser.getId(), token, tokenTimeoutSapp, TimeUnit.MINUTES);
        }

        /*if (true) {
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.OK);
            response.getHeaders().set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
            response.getHeaders().set("Access-Control-Allow-Origin", "*");
            response.getHeaders().set("Cache-Control", "no-cache");
            String body = JSONUtil.toJsonStr(Result.failed(SystemError.USER_1000));
            DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(Charset.forName("UTF-8")));
            return response.writeWith(Mono.just(buffer));
        }*/

        return chain.filter(exchange);
    }

    private boolean isExclusionUrl(String path) {
        List<String> exclusions = whiteListConfig.getUrls();
        if (exclusions.size() == 0) {
            return false;
        }
        return exclusions.stream().anyMatch(action -> antPathMatcher.match(action, path));
    }

    @Override
    public int getOrder() {
        return 0;
    }
}
